package com.spring.dao;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import com.spring.annotation.entity.Column;
import com.spring.annotation.entity.NotPersistent;
import com.spring.annotation.entity.Table;
import com.spring.bean.MyRowMapper;
import com.spring.bean.page.Page;
import com.spring.bean.page.PageView;
import com.spring.cache.eh.CacheManager;
import com.spring.exception.DaoException;
import com.spring.utils.Constants;
import com.spring.utils.StringUtils;

/**
 * @author jhc
 * Dao支持类
 */
public class DaoSupport {
	
	/** 通过id获取数据 */
	public <T> T getById(JdbcTemplate jdbcTemplate, Class<T> cls, Object id ) throws DaoException {
		return getByPro(jdbcTemplate, cls, " id = ? ", id);
	}
	
	/** 通过Pro获取数据 */
	@SuppressWarnings("unchecked")
	public <T> T getByPro(JdbcTemplate jdbcTemplate, Class<T> cls, String conditions, Object...conditionVals) throws DaoException {
		StringBuffer sql = new StringBuffer();
		sql.append(" SELECT * FROM ");
		String tableName = null;
		if(cls.isAnnotationPresent(Table.class)){
			Table t = cls.getAnnotation(Table.class);
			tableName = t.name();
		}
		if(tableName == null) {
			throw new DaoException("表名为空，不能生成sql语句");
		}
		sql.append(tableName);
		sql.append(" where ");
		sql.append(conditions);
		RowMapper rowMapper = new MyRowMapper<T>(cls);
		return (T) jdbcTemplate.queryForObject(sql.toString(), conditionVals, rowMapper);
	}
	
	/** 获取list */
	@SuppressWarnings("unchecked")
	public <T> List<T> queryForList(JdbcTemplate jdbcTemplate, Class<T> cls, String sql, Object...objs) throws DaoException {
		RowMapper rowMapper = new MyRowMapper<T>(cls);
		return jdbcTemplate.query(sql, objs, rowMapper);
	}
	
	/** 分页 : 无参*/
	public <T> PageView<T> queryForPage(JdbcTemplate jdbcTemplate, Class<T> cls, String sql, Page page) throws DaoException {
		List<Object> params = new ArrayList<Object>();
		return queryForPage(jdbcTemplate, cls, sql, page, params, false);
	}
	
	/** 分页 : 无参、可设置是否延迟加载
	 * @param needLazyLoad 是否需要延迟加载（这里只能加载一级，一般的话够用了）
	 * */
	public <T> PageView<T> queryForPage(JdbcTemplate jdbcTemplate, Class<T> cls, String sql, Page page, boolean needLazyLoad) throws DaoException {
		List<Object> params = new ArrayList<Object>();
		return queryForPage(jdbcTemplate, cls, sql, page, params, needLazyLoad);
	}
	
	/** 
	 * 分页：有参、可设置是否延迟加载
	 * @param <T>
	 * @param page 分页参数
	 * @param params 查询参数
	 * @param needLazyLoad 是否需要延迟加载（这里只能加载一级，一般的话够用了）
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> PageView<T> queryForPage(JdbcTemplate jdbcTemplate, Class<T> cls, String sql, Page page, List<Object> params, boolean needLazyLoad) throws DaoException {
		sql = sql + " LIMIT ?,?";
		params.add(page.getFirstCodeIndex());
		params.add(page.getMaxresult());
		RowMapper rowMapper = new MyRowMapper<T>(cls);
		List<T> list = jdbcTemplate.query(sql, params.toArray(), rowMapper);
		String sql_get_count = "SELECT FOUND_ROWS()";
		long totalNum = jdbcTemplate.queryForLong(sql_get_count);
		if(needLazyLoad){
			list = LazyLoading(list, jdbcTemplate);
		}
		PageView<T> pageView = new PageView<T>(list, page.getCurrentpage());
		pageView.setTotalNum(totalNum);
		return pageView;
	}
	
	/** 插入数据 */
	public boolean insert(JdbcTemplate jdbcTemplate, Object obj) throws DaoException {
		String tableName = getTableName(obj);
		if(tableName == null){
			throw new DaoException("DaoSupport - insert - " + obj.getClass()+" tableName is null ");
		}
		return execute(jdbcTemplate, tableName, obj, false, null);
	}
	
	/** 通过id更新数据 */
	public boolean update(JdbcTemplate jdbcTemplate, Object obj) throws DaoException {
		String tableName = getTableName(obj);
		Object idVal = null;
		if(tableName == null){
			throw new DaoException("DaoSupport - update - " + obj.getClass()+" tableName is null - 1 ");
		}
		try {
			idVal = PropertyUtils.getProperty(obj, "id");
		} catch (Exception e) {
			throw new DaoException("DaoSupport - update - exception : " + e.getMessage());
		}
		if(idVal == null){
			throw new DaoException("DaoSupport - update - " + obj.getClass()+" id is null - 2");
		}
		return execute(jdbcTemplate, tableName, obj, true, " id = ? ", idVal);
	}
	
	/**
	 * 自定义更新数据
	 * @param conditions - username = ? and password = ? ...
	 * @param conditionVals - Object[]
	 */
	public boolean update(JdbcTemplate jdbcTemplate, Object obj, String conditions, Object...conditionVals) throws DaoException {
		String tableName = getTableName(obj);
		if(tableName == null){
			throw new DaoException("DaoSupport - update - " + obj.getClass()+" tableName is null - 3");
		}
		return execute(jdbcTemplate, tableName, obj, true, conditions, conditionVals);
	}
	
	/**
	 * 通过id删除记录
	 * @param obj
	 */
	public boolean delete(JdbcTemplate jdbcTemplate, Object obj) throws DaoException {
		Object idVal = null;
		try {
			idVal = PropertyUtils.getProperty(obj, "id");
		} catch (Exception e) {
			throw new DaoException("DaoSupport - delete - exception : " + e.getMessage());
		} 
		if (idVal == null) {
			throw new DaoException("DaoSupport - delete - "+ obj.getClass() + " id is null");
		}
		return deleteByPro(jdbcTemplate, obj, null, " id = ? ", idVal);
	}

	/**
	 * 通过id删除记录
	 * @param tableName
	 */
	public boolean deleteById(JdbcTemplate jdbcTemplate, String tableName, Integer id) throws DaoException {
		return deleteByPro(jdbcTemplate, null, tableName, " id = ? ", id);
	}
	
	/**
	 * 通过属性删除记录
	 */
	public boolean deleteByPro(JdbcTemplate jdbcTemplate, Object obj, String tableName, String conditions, Object...objs) throws DaoException {
		String _tableName = "";
		StringBuffer sql = new StringBuffer();
		sql.append("DELETE FROM ");
		if (obj != null) {
			_tableName = getTableName(obj);
			if (tableName != null) {
				throw new DaoException("DaoSupport - deleteByPro - " + obj.getClass() + " tableName is null");
			}
		} else {
			if (tableName != null) {
				_tableName = tableName;
			} else {
				throw new DaoException("DaoSupport - deleteByPro - tableName is null ");
			}
		}
		sql.append(_tableName).append(" where ").append(conditions);
		if(objs.length > 0){
			return jdbcTemplate.update(sql.toString(), objs) > 0;
		} else {
			return false;
		}
	}
	
	/**
	 * 更新或插入操作
	 * @param tableName 表名
	 * @param obj 操作对象
	 * @param isUpdate 是否是更新操作-true更新-false插入
	 * @param conditions 更新条件 username = ? and password = ? ...
 	 * @param conditionVals 参数
	 */
	public boolean execute(JdbcTemplate jdbcTemplate, String tableName, Object obj, boolean isUpdate, String conditions, Object...conditionVals) throws DaoException {
		String executeMethod = "INSERT";
		if( isUpdate )
			executeMethod = "UPDATE";
		// 如果表名为空，则直接报错
		if( StringUtils.isBlank(tableName)) {
			throw new DaoException("DaoSupport - execute - table name is null where " + executeMethod + " with dao ");
		}
		String sql = null;
		List<Object> params = new ArrayList<Object>();	
		
		try {
			// 生成插入或者更新语句
			StringBuffer sqlBuffer = new StringBuffer( executeMethod );
			sqlBuffer.append(" ");
			sqlBuffer.append(tableName);
			sqlBuffer.append(" SET ");
			
			// 生成pro1=? ,pro2=? 的样式，同时设置他对应的参数
			keyAndParams(obj,sqlBuffer,params);
				
			sql = sqlBuffer.substring(0,sqlBuffer.length()-2);
			if( isUpdate ){
				// 如果是更新
				sql = sql +" WHERE "+ conditions;
				for(Object o : conditionVals){
					params.add(o);
				}				
			}
		} catch (Exception e) {
			throw new DaoException("DaoSupport - execute - create "+executeMethod+" sql exception : " + e.getMessage());
		}	
		return jdbcTemplate.update(sql, params.toArray()) > 0;
	}
	
	/** 生成参数部分语句 a = ?, b = ?... */
	@SuppressWarnings("rawtypes")
	private void keyAndParams(Object obj, StringBuffer sqlBuffer, List<Object> params ) throws DaoException {
		try {
			String field = null;
			Object value = null;
			// 通过beanUtils获取obj属性和属性值的键值对
			Map map;
			map = BeanUtils.describe( obj );
			// 遍历键值对
			Iterator fields = map.keySet().iterator();
			while(fields.hasNext()){
				field = (String)fields.next();
				// 获取属性值
				value = PropertyUtils.getProperty(obj, field);
				// 获取属性的数值类型
				Class c = PropertyUtils.getPropertyType(obj, field);
				// 对null过滤， 但对Double类型的不进行过滤
				if( (value == null && (c != Double.class)) || "class".equalsIgnoreCase(field) )
					continue;
				// 获取对象属性
				Field f = obj.getClass().getDeclaredField(field);
				if(f.isAnnotationPresent(NotPersistent.class)){
					continue;
				}else if(f.isAnnotationPresent(Column.class)){
					Column column = f.getAnnotation(Column.class);
					sqlBuffer.append(column.name());
				}else{
					sqlBuffer.append(field);
				}
				
				// 生成插入或者更新值，同时他的对应的值也要一一对应									
				sqlBuffer.append(" = ?, ");
				if(Constants.WEB_KEY_NULL.equals(value))
					params.add(null);
				else
					params.add(value);
			}
		} catch (Exception e) {
			throw new DaoException("DaoSupport - BeanUtils - exception : " + e.getMessage());
		} 
	}
	
	/** 通过注解取得表名 */
	private String getTableName(Object object) throws DaoException {
		if(object.getClass().isAnnotationPresent(Table.class)){
			Table e = (Table)object.getClass().getAnnotation(Table.class);
			return e.name(); 
		}
		return null;
	}
	
	/*----------------延迟加载---------------*/

	public <T> List<T> LazyLoading(List<T> list, JdbcTemplate jdbcTemplate) throws DaoException {
		return LazyLoading(list, Constants.DEFAULT_LAZY_TIME, jdbcTemplate);
	}	
	/**
	 * 延迟加载(如果需要延迟加载，则使用该方法)
	 * 手动调用，减小性能损耗
	 * @param list 需要延迟加载的list
	 * @param cls 对象类型
	 * @param exNum 需要加载几层(减小性能损耗，避免执行下一级不必要的查询操作)>=1
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public <T> List<T> LazyLoading(List<T> list, int exNum, JdbcTemplate jdbcTemplate) throws DaoException {
		if(list == null || list.size() < 1){
			return list;
		}
		Class cls = list.get(0).getClass();
		Field[] fields = cls.getDeclaredFields();
		for(Field f : fields){
			if(f.isAnnotationPresent(NotPersistent.class)){
				NotPersistent np = f.getAnnotation(NotPersistent.class);
				String fk = np.fk();
				String key = np.key();
				if(StringUtils.isBlank(fk)){
					continue;
				}
				// 获取字段类型
				Class f_cls = f.getType();
				Object fkVal = null;
				Object keyVal = null;
				for(T t : list){
					Object o = null;
					try {
						if(StringUtils.isBlank(key)){
							fkVal = PropertyUtils.getProperty(t, fk);
							o = LazyLoading(getById(jdbcTemplate, f_cls, fkVal), exNum, jdbcTemplate);
						} else {
							keyVal = PropertyUtils.getProperty(t, key);
							o = LazyLoading(getByPro(jdbcTemplate, f_cls, fk, keyVal), exNum, jdbcTemplate);
						}
						PropertyUtils.setProperty(t, f.getName(), o);
					} catch (Exception e) {
						throw new DaoException("DaoSupport - LazyLoading - List<T> - exception : " + e.getMessage());
					}
				}
			}
		}
		return list;
	}
	
	public <T> T LazyLoading(T t, JdbcTemplate jdbcTemplate) throws DaoException {
		return LazyLoading(t, Constants.DEFAULT_LAZY_TIME, Constants.LAZY_LEVEL_CON, jdbcTemplate);
	}
	
	public <T> T LazyLoading(T t, int exNum, JdbcTemplate jdbcTemplate) throws DaoException {
		return LazyLoading(t, exNum, Constants.LAZY_LEVEL_CON, jdbcTemplate);
	}
	/**
	 * 延迟加载(如果需要延迟加载，则使用该方法)
	 * 手动调用，减小性能损耗
	 * @param t 需要延迟加载的对象
	 * @param cls 对象类型
	 * @param exNum 需要加载几层(减小性能损耗，避免执行下一级不必要的查询操作),默认值为1
	 * @param calNum 计数器(通过和exNum比较，来限制加载层数, 默认值为0)
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public <T> T LazyLoading(T t, int exNum, int calNum, JdbcTemplate jdbcTemplate) throws DaoException {
		if(t == null || exNum > calNum){
			return t;
		}
		// 计数器，执行一次就加一
		++calNum;
		Class cls = t.getClass();
		Field[] fields = cls.getDeclaredFields();
		for(Field f : fields){
			if(f.isAnnotationPresent(NotPersistent.class)){
				NotPersistent np = f.getAnnotation(NotPersistent.class);
				String fk = np.fk();
				String key = np.key();
				if(StringUtils.isBlank(fk)){
					continue;
				}
				Class f_cls = f.getType();
				Object fkVal = null;
				Object keyVal = null;
				Object o = null;
				try {
					if(StringUtils.isBlank(key)){
						fkVal = PropertyUtils.getProperty(t, fk);
						o = LazyLoading(getById(jdbcTemplate, f_cls, fkVal), exNum, calNum, jdbcTemplate);
					} else {
						keyVal = PropertyUtils.getProperty(t, key);
						o = LazyLoading(getByPro(jdbcTemplate, f_cls, fk, keyVal), exNum, calNum, jdbcTemplate);
					}
					PropertyUtils.setProperty(t, f.getName(), o);
				} catch (Exception e) {
					throw new DaoException("DaoSupport - LazyLoading - T - exception : " + e.getMessage());
				}
			}
		}
		return t;
	}
	
	/*------------------缓存--------------------*/
	
	/**
	 * getCacheById
	 */
	@SuppressWarnings("unchecked")
	public <T> T getCacheById(JdbcTemplate jdbcTemplate, String cache, Class<T> cls, Object id) throws DaoException {
		T t = null;
		// 缓存名称
		String className = cls.getName();
		if(id == null) {
			throw new DaoException("DaoSupport - getCacheById - " + className + " id is null ");
		}
		// id即使key
		String cacheKey = id.toString();
		t = (T) CacheManager.get(className, cacheKey);
		if(t == null){
			t = getById(jdbcTemplate, cls, id);
			CacheManager.set(className, cacheKey, (Serializable)t);
		}
		return t;
	}
	
	/**
	 * queryCacheForList
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> queryCacheForList(JdbcTemplate jdbcTemplate, Class<T> cls, String sql, Object...objs) throws DaoException {
		List<T> list = null;
		// 缓存名称
		String className = cls.getName();
		// key
		String cacheKey = Constants.CACHE_LIST_SIGN + ArrayToString(objs);
		list = (List<T>) CacheManager.get(className, cacheKey);
		if(list == null){
			list = queryForList(jdbcTemplate, cls, sql, objs);
			CacheManager.set(className, cacheKey, (Serializable)list);
		}
		return list;
	}
	
	/**
	 * queryCacheForPage 带参
	 */
	@SuppressWarnings("unchecked")
	public <T> PageView<T> queryCacheForPage(JdbcTemplate jdbcTemplate, Class<T> cls, String sql, Page page, List<Object> params, boolean needLazyLoad) throws DaoException {
		PageView<T> pageView  = null;
		String className = cls.getName();
		String cacheKey = Constants.CACHE_PAGE_SIGN + page.toString() + ListToString(params);
		pageView = (PageView<T>) CacheManager.get(className, cacheKey);
		if(pageView == null){
			pageView = queryForPage(jdbcTemplate, cls, sql, page, params, needLazyLoad);
			CacheManager.set(className, cacheKey, (Serializable)pageView);
		}
		return pageView;
	}
	
	/**
	 * queryCacheForPage 不带参
	 */
	@SuppressWarnings("unchecked")
	public <T> PageView<T> queryCacheForPage(JdbcTemplate jdbcTemplate, Class<T> cls, String sql, Page page, boolean needLazyLoad) throws DaoException {
		PageView<T> pageView  = null;
		String className = cls.getAnnotation(Table.class).name();
		String cacheKey = Constants.CACHE_PAGE_SIGN + page.toString();
		pageView = (PageView<T>) CacheManager.get(className, cacheKey);
		if(pageView == null){
			pageView = queryForPage(jdbcTemplate, cls, sql, page, needLazyLoad);
			CacheManager.set(className, cacheKey, (Serializable)pageView);
		}
		return pageView;
	}
	
	/**
	 * 将数组转换为字符串,用作查询缓存的key
	 */
	private String ArrayToString(Object[] objs){
		if(objs.length < 1){
			return "";
		}
		StringBuffer str = new StringBuffer();
		for(int i=0; i<objs.length; i++){
			str.append(objs[i].toString());
		}
		return str.toString();
	}
	
	/**
	 * 将list转换为字符串,用作查询缓存的key
	 */
	private String ListToString(List<Object> list){
		StringBuffer str = new StringBuffer();
		for(int i=0; i<list.size(); i++){
			str.append(list.get(i).toString());
		}
		return str.toString();
	}
	
}
